package com.liming.service.wx.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.liming.service.http.HttpClient;
import com.liming.service.http.enums.UrlEnum;
import com.liming.service.wx.WxClient;
import com.liming.service.wx.config.WxJsapiConfig;
import com.liming.service.wx.config.WxMiniLoginParam;
import com.liming.service.wx.pojo.vo.PrepayVO;
import com.wechat.pay.java.core.cipher.SignatureResult;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

/**
 * @author liming
 * @date 2025/4/9
 */
@Slf4j
@Service
@AllArgsConstructor
public class WxClientImpl implements WxClient {

    private final WxMiniLoginParam vxMiniLoginParam;
    private final WxJsapiConfig wxJsapiConfig;
    private final JsapiService wechatJsapiService;
    private final RefundService wechatRefundService;
    private final HttpClient httpClient;
    private final Signer mySigner;


    public String wxMiniLogin(String jsCode) {
        String[] str = new String[]{"appid", "secret"};
        Map<String, Object> from = BeanUtil.beanToMap(vxMiniLoginParam, str);
        from.put("js_code", jsCode);
        from.put("grant_type", "authorization_code");
        String body = httpClient.requestByEnumWithForm(UrlEnum.WX_MINI_LOGIN, from);
        JSONObject response = JSONUtil.parseObj(body);
        log.info("信息，response:{}, openid:{}", response, response.getStr("openid"));
        return response.getStr("openid");
    }

    /**
     * 30分钟
     *
     * @return RFC3339
     */
    private static String getTimeExpire() {
        ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
        ZonedDateTime expireTime = now.plusMinutes(30);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
        return expireTime.format(formatter);
    }

    private static String getNonceStr() {
        return RandomUtil.randomString(20);
    }

    private static String getTimeStamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    @Override
    public PrepayVO wxJsapiPay(String openid, String orderNo) {
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        request.setAppid(wxJsapiConfig.getAppid());
        request.setMchid(wxJsapiConfig.getMerchantId());
        request.setDescription("测试商品标题");
        request.setOutTradeNo(orderNo);
        request.setTimeExpire(getTimeExpire());
        // 附件，查询订单API和支付成功回调通知都会返回给商户
        request.setAttach("");
        // 商户后端回调通知的URL、确保能被外网访问
        request.setNotifyUrl("https://notify_url");
        // 传入true时，支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能，传此字段才可生效。
        request.setSupportFapiao(true);
        Amount amount = new Amount();
        amount.setTotal(1);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        // 支付者(openid)
        Payer payer = new Payer();
        payer.setOpenid(openid);
        request.setPayer(payer);
        PrepayResponse response = wechatJsapiService.prepay(request);
        PrepayVO prepayVO = new PrepayVO();
        prepayVO.setSignType("RSA");
        prepayVO.setTimeStamp(getTimeStamp());
        prepayVO.setNonceStr(getNonceStr());
        prepayVO.setPrepayPackage("prepay_id=" + response.getPrepayId());
        // 用于toString
        prepayVO.setAppid(wxJsapiConfig.getAppid());
        paySign(prepayVO);
        return prepayVO;
    }

    @Override
    public PrepayVO wxJsapiRepay(String prepayPackage) {
        PrepayVO result = new PrepayVO();
        result.setAppid(wxJsapiConfig.getAppid());
        result.setSignType("RSA");
        result.setTimeStamp(getTimeStamp());
        result.setNonceStr(getNonceStr());
        result.setPrepayPackage(prepayPackage);
        paySign(result);
        return result;
    }

    @Override
    public Transaction wxJsapiQueryOrderByOutTradeNo(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxJsapiConfig.getMerchantId());
        request.setOutTradeNo(outTradeNo);
        return wechatJsapiService.queryOrderByOutTradeNo(request);
    }

    @Override
    public void wxJsapiCloseOrderByOutTradeNo(String outTradeNo) {
        CloseOrderRequest request = new CloseOrderRequest();
        request.setMchid(wxJsapiConfig.getMerchantId());
        request.setOutTradeNo(outTradeNo);
        wechatJsapiService.closeOrder(request);
    }

    private void paySign(PrepayVO prepayVO) {
        SignatureResult signResult = mySigner.sign(prepayVO.toString());
        prepayVO.setPaySign(signResult.getSign());
    }

    @Override
    public Refund wxRefundWithRefundNoByOrderNo(String orderNo, String refundNo) {
        CreateRequest request = new CreateRequest();
        request.setOutTradeNo(orderNo);
        request.setOutRefundNo(refundNo);
        AmountReq amount = new AmountReq();
        amount.setRefund(1L);
        amount.setTotal(1L);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setNotifyUrl("https://notify_url");
        return wechatRefundService.create(request);
    }

    @Override
    public Refund wxRefundQueryByRefundNo(String refundNo) {
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(refundNo);
        return wechatRefundService.queryByOutRefundNo(request);
    }
}
